#include <string>
#include <iostream>
#include <typeinfo>
#include <type_traits>

template <typename T>
void f1(T &param)
{
    // Get type info
    const std::type_info &ti1 = typeid(param);
    std::cout << param << ", " << ti1.name() << std::endl;
}

template <typename T>
void f2(T &&param)
{ // Get type info
    const std::type_info &ti1 = typeid(param);
    std::cout << param << ", " << ti1.name() << std::endl;
}

template <typename T>
void f3(T param)
{ // Get type info
    const std::type_info &ti1 = typeid(param);
    std::cout << param << ", " << ti1.name() << std::endl;
}

template <typename T>
void testUniversalRef(T&& str_test){
    std::remove_reference_t<T> another_str(std::forward<T>(str_test));
    std::cout << another_str << ", address is " << &another_str << "\n";
}

template <typename T>
void testUniversalRefOfUptr(T&& uptr_test){
    std::remove_reference_t<T> another_uptr(std::forward<T>(uptr_test));
    std::cout << another_uptr.get() << ", content is " << *another_uptr << "\n";
}